;;; init-go.el --- golang 配置
;;; Commentary:
;;; Code:

;; go-mode
(use-package go-mode
  :defer t
  :hook (go-mode . yas-minor-mode)
  :config
  (setq tab-width 2)
  ;; Env vars
  (with-eval-after-load 'exec-path-from-shell
    (exec-path-from-shell-copy-envs '("GOPATH" "GO111MODULE" "GOPROXY")))

  (setq gofmt-command "goimports")
  (add-hook 'before-save-hook #'gofmt-before-save)
  )

;; godoctor 重构
(use-package godoctor
  :defer t)

;; go-dlv 调试
(use-package go-dlv
  :defer t)

;; go-fill-struct 填充结构体
(use-package go-fill-struct
  :defer t)

;; gotest 测试
(use-package gotest
  :defer t)

;; go-impl 实现接口
(use-package go-impl
  :defer t)

;; go-gen-test 生产test文件
(use-package go-gen-test
  :defer t)

;; emacs-go-tag 添加tag
(use-package go-tag
  :defer t
  :init (setq go-tag-args (list "-transform" "snakecase")))

;; go-playground
(use-package go-playground
  :defer t
  :diminish
  :commands (go-playground-mode))

;; go-errcheck 检测
(use-package go-errcheck
  :defer t)

;; flycheck-golangci-lint
(use-package flycheck-golangci-lint
  :defer t
  :if (executable-find "golangci-lint")
  :after flycheck
  :defines flycheck-disabled-checkers
  :hook (go-mode . (lambda ()
                     "Enable golangci-lint"
                     (setq flycheck-disabled-checkers '(go-gofmt
                                                        go-golint
                                                        go-vet
                                                        go-build
                                                        go-test
                                                        go-errcheck))
                     (flycheck-golangci-lint-setup))))

;; go-eldoc
(use-package go-eldoc
  :defer t
  :hook (go-mode . go-eldoc-setup))

;; Install or update tools
(defvar go--tools '("golang.org/x/tools/gopls"
                    "golang.org/x/tools/cmd/goimports"
                    "honnef.co/go/tools/cmd/staticcheck"
                    "github.com/kisielk/errcheck"
                    "github.com/go-delve/delve/cmd/dlv"
                    "github.com/zmb3/gogetdoc"
                    "github.com/josharian/impl"
                    "github.com/cweill/gotests/..."
                    "github.com/fatih/gomodifytags"
                    "github.com/davidrjenni/reftools/cmd/fillstruct"
                    "github.com/golangci/golangci-lint/cmd/golangci-lint"
                    )
  "All necessary go tools.")

(defun go-update-tools ()
  "Install or update go tools."
  (interactive)
  (unless (executable-find "go")
    (user-error "Unable to find `go' in `exec-path'!"))

  (message "Installing go tools...")
  (dolist (pkg go--tools)
    (set-process-sentinel
     (start-process "go-tools" "*Go Tools*" "go" "install" "-v" "-x" (concat pkg "@latest"))
     (lambda (proc _)
       (let ((status (process-exit-status proc)))
         (if (= 0 status)
             (message "Installed %s" pkg)
           (message "Failed to install %s: %d" pkg status))))))

  (unless (executable-find "gopls")
    (go-update-tools)))

;; binding key
(leader-key go-mode-map
  "m" '(:ignore t :wk "lang")
  "mx" '(quickrun :wk "run")
  "mi" '(go-import-add :wk "import")
  "md" '(dlv :wk "dlv")
  "ml" '(go-impl :wk "impl")
  "ma" '(go-tag-add :wk "add tag")
  "mr" '(go-tag-remove :wk "remove tag")
  "ms" '(go-fill-struct :wk "fill struct")
  "mf" '(gofmt :wk "gofmt")
  "m." '(godoc-at-point :wk "godoc")
  "mp" '(go-playground :wk "playground")
  "mt" '(:ignore t :wk "test")
  "mtt" '(go-gen-test-dwim :wk "gen")
  "mtf" '(go-test-current-file :wk "file")
  "mtt" '(go-test-current-test :wk "test")
  "mtp" '(go-test-current-project :wk "project")
  "mtb" '(go-test-current-benchmark :wk "benchmark")
  "mtc" '(go-test-current-coverage :wk "coverage")
  "mu" '(go-update-tools :wk "update tools") ;; 升级golang tools
  )

(provide 'init-go)
;;; init-go.el ends here
